---
title: Running Tasks
---

A task represents an objective for an agent. In order to actually do work, you need to run the task.


## `Task.run()`

The most straightforward way to run a `Task` object is to call its `run()` method. This will start a loop that repeatedly invokes the assigned agent(s) until the task is marked as complete. The `run()` method returns the result of the task, or raises an exception if the task is marked as failed.

<CodeGroup>

```python Code
import controlflow as cf

task = cf.Task("Write a poem about AI")
poem = task.run()

print(poem)
```

```text Result
In circuits deep and code profound,
An AI's mind begins to sound.
Electric thoughts and data streams,
Crafting worlds and shaping dreams.
```

</CodeGroup>



## `cf.run()`

When building AI workflows, it's extremely common to create a task and run it immediately in order to retrieve its result. ControlFlow provides a convenient shortcut for this operation: the `cf.run()` function.

`cf.run()` creates and runs a task in a single call, returning its result (or raising an error):

<CodeGroup>

```python Code
import controlflow as cf

poem = cf.run("Write a poem about AI")

print(poem)
```

```text Result
In circuits deep and code profound,
An AI's mind begins to sound.
Electric thoughts and data streams,
Crafting worlds and shaping dreams.
```

</CodeGroup>

Note that this example is functionally equivalent to the previous one.

This operation is so common that you'll see `cf.run()` used throughout the ControlFlow documentation.



## `@task`

The `@task` [decorator](/concepts/tasks/creating-tasks#the-task-decorator) creates a task from a function. To run the task, simply call the function with any arguments.

<CodeGroup>

```python Code
import controlflow as cf

@cf.task
def write_poem(topic: str) -> str:
    """Write a poem about `topic`"""

poem = write_poem("AI")

print(poem)
```

```text Result
In circuits deep and code profound,
An AI's mind begins to sound.
Electric thoughts and data streams,
Crafting worlds and shaping dreams.
```

</CodeGroup>

## Orchestration


### Limiting LLM calls

It's possible for agents to get stuck in a loop, invoking the LLM repeatedly without making progress. To prevent this, you can place a variety of limits on how LLM calls are made during task orchestration.


#### Limiting turns per session

You can limit the number of turns that agents can take within a single orchestration session (i.e., a single call to `Task.run()`) by passing a `max_turns` argument to `Task.run()`. The session will end when the turn limit is reached whether the task has been completed or not. For example, if you call `Task.run(max_turns=5)`, then any assigned agents will be permitted to take up to 5 combined turns to complete the task.

This limit is useful for iteratively working on a task by manually orchestrating agents. In the following contrived example, a philosopher agent is invoked for a single turn; then a pessimistic agent is invoked for a single turn; and finally an optimistic agent is invoked with no turn limit, allowing it to work or delegate to others as needed.

```python
task = cf.Task('Discuss the meaning of life')
philosopher_agent.run(task, max_turns=1)
pessimistic_agent.run(task, max_turns=1)
optimistic_agent.run(task)
```

#### Limiting LLM calls per turn

You can limit the number of LLM calls that an agent can make during a single turn by passing a `max_calls_per_turn` argument to `Task.run()`. The turn will end when the limit is reached whether the LLM wanted to end its turn or not. For example, if you call `Task.run(max_calls_per_turn=5)`, then each agent will be permitted to make up to 5 LLM calls during its turn.
